﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

using LowLevelGraphics.Filter;
using LowLevelGraphics.Analysis;

namespace LowLevelGraphics.Extractors
{
    /// <summary>
    /// 
    /// </summary>
    public enum AUTOCROP_MODE
    {
        /// <summary>
        /// 
        /// </summary>
        CUSTOM,

        /// <summary>
        /// 
        /// </summary>
        DEFAULT,

        /// <summary>
        /// 
        /// </summary>
        COLORDEPENDENCYLINES,

        /// <summary>
        /// 
        /// </summary>
        RGBFILTER_METHOD,
    }

    /// <summary>
    /// Use this extractor for automatically CUT and RE-CENTER pics.
    /// First, there will be a filter that estimates unimportant background informations
    /// Then the minimum and maximum will be calculated. 
    /// The rectangle reported is containing information that stays.
    /// </summary>
    public class AutoCrop : IBaseForeBackgroundColorFilter
    {
        protected Color m_ColorForeground = Definitions.White;
        protected Color m_ColorBackground = Definitions.Black;
        private List<IFilter> m_aFilter = new List<IFilter>();
        private Rectangle m_Rectangle = Rectangle.Empty;
        private AUTOCROP_MODE M_AUTOCROP_MODE = AUTOCROP_MODE.DEFAULT;
        private Pen m_Pen = Pens.Red;
        private bool m_bDrawRectangle = true;

        /// <summary>
        /// Initializes a new instance of the <see cref="AutoCrop"/> class.
        /// </summary>
        public AutoCrop()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AutoCrop"/> class.
        /// </summary>
        /// <param name="_aFilter">The _a filter.</param>
        public AutoCrop(List<IFilter> _aFilter)
        {
            m_aFilter = _aFilter;
        }

        /// <summary>
        /// Return filterlist
        /// </summary>
        public List<IFilter> FilterList
        {
            get { return m_aFilter; }
        }

        /// <summary>
        /// Returns the crop rectangle
        /// </summary>
        public Rectangle CropRectangle
        {
            get { return m_Rectangle; }
        }

        /// <summary>
        /// Background Color
        /// </summary>
        public Color BackgroundColor
        {
            get { return m_ColorBackground; }
            set { m_ColorBackground = value; }
        }

        /// <summary>
        /// Foreground Color
        /// </summary>
        public Color Foreground
        {
            get { return m_ColorForeground; }
            set { m_ColorForeground = value; }
        }

        /// <summary>
        /// Gets or sets the pen.
        /// </summary>
        /// <value>The pen.</value>
        public Pen Pen 
        {
            get { return m_Pen; }
            set { m_Pen = value; }
        }

        /// <summary>
        /// Gets or sets the AUTOCRO p_ MODE.
        /// </summary>
        /// <value>The AUTOCRO p_ MODE.</value>
        public AUTOCROP_MODE AUTOCROP_MODE
        {
            get { return M_AUTOCROP_MODE; }
            set { M_AUTOCROP_MODE = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [draw rectangle].
        /// </summary>
        /// <value><c>true</c> if [draw rectangle]; otherwise, <c>false</c>.</value>
        public bool DrawRectangle
        {
            get { return m_bDrawRectangle; }
            set { m_bDrawRectangle = value; }
        }

        /// <summary>
        /// Crops the specified _bitmap.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <returns></returns>
        public UnsafeBitmap Crop(UnsafeBitmap _bitmap)
        {
            Rectangle rectangleCrop = Rectangle.Empty;

            switch (M_AUTOCROP_MODE)
            {
                case AUTOCROP_MODE.DEFAULT:
                    DefaultExtraction(ref _bitmap, ref rectangleCrop);
                    break;

                case AUTOCROP_MODE.COLORDEPENDENCYLINES:
                    rectangleCrop = ColorDependencyLinesCrop(_bitmap);
                    break;

                case AUTOCROP_MODE.RGBFILTER_METHOD:
                    UnsafeBitmap bitmapCloned = (UnsafeBitmap)_bitmap.Clone();
                    new RGBFilter2().Execute(bitmapCloned);
                    List<Point> aPoint = new List<Point>();
                    new PerPixelFilter(new PerPixelFilter.PerPixelFilterDelegate(
                        delegate(Color _color)
                        {
                            _color = ColorFunctions.DistanceSquared(_color, m_ColorBackground) < 100 ? m_ColorBackground : m_ColorForeground;
                            return _color; 
                        })).Execute(bitmapCloned);

                    int nHeight = _bitmap.Height;
                    int nWidth = _bitmap.Width;
                    for (int y = 0; y < nHeight; y++)
                    {
                        for (int x = 0; x < nWidth; x++)
                        {
                            if (bitmapCloned.GetPixel(x, y) == m_ColorForeground)
                            {
                                aPoint.Add(new Point(x, y));
                            }
                        }
                    }
                    rectangleCrop = new BoundingBoxEstimator(aPoint).Estimate(bitmapCloned);
                    break;

                default:
                    foreach (IFilter filter in m_aFilter)
                    {
                        _bitmap = filter.ExecuteCloned(_bitmap);
                    }
                    break;
            }

            m_Rectangle = rectangleCrop;

            _bitmap.Dispose();
            if (m_bDrawRectangle)
            {
                Graphics g = Graphics.FromImage(_bitmap.InternalBitmap);
                g.DrawRectangle(m_Pen , rectangleCrop);
                g.Dispose();
            }
            if (!m_bDrawRectangle)
            {
                _bitmap = BitmapFunctions.Crop(_bitmap.InternalBitmap, rectangleCrop);
            }

            return _bitmap;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        private static Rectangle ColorDependencyLinesCrop(UnsafeBitmap _bitmap)
        {
            _bitmap.UnlockImage();
            Bitmap bitmap = (Bitmap)_bitmap.InternalBitmap.Clone();
            DrawPixelColorDependencyLine drawPixelColorDependencyLine = new DrawPixelColorDependencyLine(bitmap);
            drawPixelColorDependencyLine.Analyse();

            int nMinX = int.MaxValue;
            int nMaxX = -1;

            int nMinY = int.MaxValue;
            int nMaxY = -1;

            foreach(Point pt in drawPixelColorDependencyLine.ConnectionPoints)
            {
                int nX = pt.X;
                int nY = pt.Y;

                if (nX < nMinX) nMinX = nX;
                if (nX > nMaxX) nMaxX = nX;

                if (nY < nMinY) nMinY = nY;
                if (nY > nMaxY) nMaxY = nY;
            }

            Rectangle rectangleCrop = Rectangle.FromLTRB(nMinX, nMinY, nMaxX, nMaxY);
            //TODO: be able to draw a single color dependency line, or get the coordinates, 
            //than we are able to determine the rectangle here...
            return rectangleCrop;
        }

        /// <summary>
        /// Defaults the extraction.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="rectangleCrop">The rectangle crop.</param>
        private void DefaultExtraction(ref UnsafeBitmap _bitmap, ref Rectangle rectangleCrop)
        {
            EstimateBoundingBox estimateBackground = new EstimateBoundingBox(Color.Transparent);
            _bitmap = estimateBackground.ExecuteCloned(_bitmap);
            List<Range> aLeftRange = estimateBackground.LeftRanges;
            List<Range> aRightRange = estimateBackground.RightRanges;

            aLeftRange.Sort(new RangeComparer(RANGE_COMPARER_MODE.EndX));
            aRightRange.Sort(new RangeComparer(RANGE_COMPARER_MODE.StartX));

            int nLeft = aLeftRange[0].EndX;

            int nRight = aRightRange[aRightRange.Count - 1].StartX;

            rectangleCrop = new Rectangle(nLeft, 0, nRight, _bitmap.Height - 1);
        }
    }
}
